﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using Apache.Cassandra;
using System.Data.Cassandra.Pool;
using System.Data.Cassandra.Connection.Pool;

namespace System.Data.Cassandra.Connection.Nodes
{
    public class RingNodeEnumerator : ManualNodeEnumerator
    {
        private bool disableUpdateNodes;

        private static readonly Random Random = new Random();

        public RingNodeEnumerator(ConnectionPool pool)
            : base(pool)
        {
            if (pool.Keyspace == "system")
            {
                disableUpdateNodes = true;
            }
        }

        public override void UpdateNodes()
        {
            if (!disableUpdateNodes)
            {
                ConnectionPool pool = Pool;
                Apache.Cassandra.Cassandra.Client client = pool.Borrow();
                List<TokenRange> source;
                try
                {
                    source = client.describe_ring(pool.Keyspace);
                    pool.Return(client);
                }
                catch (Exception ex)
                {
                    if (ex is InvalidRequestException)
                    {
                        pool.Return(client);
                        disableUpdateNodes = true;
                        return;
                    }
                    pool.ErrorReturn(client, ex);
                    throw;
                }
                List<String> list = source.SelectMany(tr => tr.Endpoints).Distinct().ToList();
                HashSet<IPAddress> hashSet = new HashSet<IPAddress>();
                foreach (string current in Nodes)
                {
                    try
                    {
                        IPAddress[] hostAddresses = Dns.GetHostAddresses(current);
                        hashSet.UnionWith(hostAddresses);
                    }
                    catch (SocketException)
                    {
                        disableUpdateNodes = true;
                        return;
                    }
                }
                hashSet.RemoveWhere(ipa => IPAddress.IsLoopback(ipa));
                if (hashSet.Count > 0)
                {
                    IEnumerable<IPAddress> other = list.SelectMany(host => Dns.GetHostAddresses(host));
                    hashSet.IntersectWith(other);
                    if (hashSet.Count == 0)
                    {
                        disableUpdateNodes = true;
                        return;
                    }
                }
                SetNodes(list);
                int count = Nodes.Count;
                if (count > 1)
                {
                    for (int i = Random.Next(count); i > 0; i--)
                    {
                        RoundRobin();
                    }
                }
            }
        }
    }
}